package com.alameda.service.menu;

import com.alameda.api.menu.MenuService;
import com.alameda.api.user.UserService;
import com.alameda.base.constant.DataPermTypeConstant;
import com.alameda.mapper.menu.MenuMapper;
import com.alameda.menu.bo.MenuBO;
import com.alameda.menu.bo.MenuExportBO;
import com.alameda.menu.dto.MenuDTO;
import com.alameda.menu.pojo.MenuDO;
import com.alameda.menu.vo.MenuVO;
import com.alameda.user.pojo.UserDO;
import com.alameda.utils.*;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 菜单 业务逻辑层 具体实现
 *
 * @author Lv
 * @date 2024/05/09
 */
@Slf4j
@Service
public class MenuServiceImpl extends ServiceImpl<MenuMapper, MenuDO> implements MenuService {

    @Autowired
    private MenuMapper menuMapper;

    @Autowired
    private UserService userService;

    /**
     * 读取菜单树
     *
     * @return
     */
    @Override
    public List<MenuVO> loadMenu() {
        Long userId = ThreadLocalUtils.getUserId();
        UserDO userDO = userService.getById(userId);

        List<MenuDO> menuDOList = null;
        Integer power = userDO.getPower();

        if (DataPermTypeConstant.DATA_TYPE_USER.equals(String.valueOf(power))) {
            menuDOList = this.list(MenuDO.builder().build());
        } else {
            menuDOList = this.lambdaQuery().in(MenuDO::getMenuPower, power).list();
        }

        log.info("菜单数据menuDOList :{}", JSONObject.toJSONString(menuDOList));
        return convertToTreeMenuList(menuDOList);
    }

    /**
     * 读取菜单信息列表
     *
     * @param menuDTO 菜单数据传递对象
     * @return
     */
    @Override
    public IPage<MenuVO> listMenuInfo(MenuDTO menuDTO) {
        IPage<MenuVO> page = new Page<>(menuDTO.getPageNum(), menuDTO.getPageSize());
        return menuMapper.findByMenuNameLike(page, menuDTO.getMenuName());
    }

    /**
     * 导出excel
     *
     * @param menuDTO 菜单数据传递对象
     */
    @Override
    public void export(MenuDTO menuDTO) {
        List<MenuExportBO> menuExportBOList = getExportData(menuDTO);
        ExcelUtils.export(menuExportBOList, MenuExportBO.class, "菜单列表");
    }

    /**
     * 导出菜单json
     */
    @Override
    public void exportMenu() {
        List<MenuVO> menuVOList = loadMenu();
        List<MenuBO> menuBOList = CollectionUtils.newArrayList();

        menuVOList.forEach(item -> {
            List<MenuVO> childrenList = item.getChildren();
            List<MenuBO.Children> menuChildrenList = new ArrayList<>();

            childrenList.forEach(itemChildren -> {
                MenuBO.Meta meta = MenuBO.Meta.builder().build();
                meta.setTitle(itemChildren.getMenuName());
                meta.setIcon(itemChildren.getMenuIcon());
                meta.setNoCache(Boolean.TRUE.toString());

                MenuBO.Children children = MenuBO.Children.builder().build();
                children.setPath(itemChildren.getMenuPath());
                children.setComponent(itemChildren.getMenuComponent());
                children.setName(PubUtils.convertTransverseToUpCamelCase(itemChildren.getMenuPath()));
                children.setMeta(meta);

                menuChildrenList.add(children);
            });

            MenuBO.Meta meta = MenuBO.Meta.builder().build();
            meta.setTitle(item.getMenuName());
            meta.setIcon(item.getMenuIcon());
            meta.setNoCache(Boolean.TRUE.toString());

            MenuBO menuBO = MenuBO.builder().build();
            menuBO.setPath(item.getMenuPath());
            menuBO.setName(PubUtils.convertTransverseToUpCamelCase(item.getMenuPath()));
            menuBO.setComponent(Optional.ofNullable(item.getMenuComponent()).orElse("Layout"));
            menuBO.setRedirect(item.getMenuPath());
            menuBO.setAlwaysShow(Boolean.TRUE);
            menuBO.setHidden(Boolean.TRUE);
            menuBO.setMeta(meta);
            menuBO.setChildren(menuChildrenList);
            menuBO.setMeta(meta);

            menuBOList.add(menuBO);
        });

        log.info("导出菜单信息:{}", JSONObject.toJSONString(menuBOList));

        TextUtils.downloadText(JSONObject.toJSONString(menuBOList), "菜单信息", HttpUtils.getHttpServletResponse());
    }

    /**
     * 删除菜单
     *
     * @param idList 菜单id集合
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean removeByIdList(List<Long> idList) {
        List<MenuDO> menuDOList = this.findByIdInOrderByCreateTimeDesc(idList);

        List<Long> menuParentIdList = new ArrayList<>();
        menuDOList.forEach(item -> {
            Long menuParentId = item.getMenuParentId();
            if (PubUtils.isNull(menuParentId)) {
                menuParentIdList.add(menuParentId);
            }
        });

        List<MenuDO> childMenuList = findChildList(menuParentIdList);

        List<Long> childMenuIdList = childMenuList.stream().map(MenuDO::getId)
                .filter(item -> !idList.contains(item))
                .collect(Collectors.toList());

        this.removeByIds(childMenuIdList);
        return this.removeByIds(idList);
    }

    /**
     * 获取子菜单信息
     *
     * @param menuParentIdList 菜单父类id
     * @return
     */
    private List<MenuDO> findChildList(List<Long> menuParentIdList) {
        return this.lambdaQuery().in(MenuDO::getMenuParentId, menuParentIdList).list();
    }

    /**
     * 获取导出数据
     *
     * @param menuDTO 菜单数据传递对象
     * @return
     */
    private List<MenuExportBO> getExportData(MenuDTO menuDTO) {
        if (PubUtils.isEmpty(menuDTO.getIdList())) {
            IPage<MenuVO> pageData = listMenuInfo(menuDTO);
            return ObjectUtils.convertList(pageData.getRecords(), MenuExportBO.class);
        } else {
            List<MenuDO> menuDOList = this.findByIdInOrderByCreateTimeDesc(menuDTO.getIdList());
            return ObjectUtils.convertList(menuDOList, MenuExportBO.class);
        }
    }

    /**
     * 数据模型转换
     *
     * @param menuDOList 菜单列表
     * @return
     */
    private List<MenuVO> convertToTreeMenuList(List<MenuDO> menuDOList) {
        List<MenuVO> menuVOList = new ArrayList<>();

        for (MenuDO menuDO : menuDOList) {
            MenuVO menuVO = convertToMenuVO(menuDO);
            if (PubUtils.isNull(menuVO.getMenuParentId())) {
                recursionFn(menuVO, menuDOList);
                menuVOList.add(menuVO);
            }
        }

        menuVOList = menuVOList.stream().sorted(Comparator.comparing(MenuVO::getMenuOrderNumber)).collect(Collectors.toList());
        log.info("menuVOList :{}", JSONObject.toJSONString(menuVOList));
        return menuVOList;
    }

    /**
     * 数据模型转换
     *
     * @param menuDO 菜单对象
     * @return
     */
    private MenuVO convertToMenuVO(MenuDO menuDO) {
        return ObjectUtils.convert(menuDO, MenuVO.class);
    }

    /**
     * 将菜单转换为树形结构(递归算法)
     *
     * @param menuVO     菜单视图对象
     * @param menuDOList 菜单数据集合
     */
    private void recursionFn(MenuVO menuVO, List<MenuDO> menuDOList) {
        List<MenuVO> childList = getChildList(menuVO, menuDOList);
        menuVO.setChildren(childList);
        for (MenuVO vo : childList) {
            if (hasChild(vo, menuDOList)) {
                recursionFn(vo, menuDOList);
            }
        }
    }

    /**
     * 是否存在子菜单
     *
     * @param menuVO     菜单视图对象
     * @param menuDOList 菜单数据集合
     * @return
     */
    private boolean hasChild(MenuVO menuVO, List<MenuDO> menuDOList) {
        return getChildList(menuVO, menuDOList).size() > 0;
    }

    /**
     * 获取子类菜单
     *
     * @param menuVO     菜单视图对象
     * @param menuDOList 菜单数据集合
     * @return
     */
    private List<MenuVO> getChildList(MenuVO menuVO, List<MenuDO> menuDOList) {
        List<MenuVO> menuVOList = new ArrayList<>();

        for (MenuDO menuDO : menuDOList) {
            if (PubUtils.isNull(menuDO.getMenuParentId())) {
                continue;
            }

            if (menuDO.getMenuParentId().equals(menuVO.getId())) {
                MenuVO convert = convertToMenuVO(menuDO);
                menuVOList.add(convert);
            }
        }

        return menuVOList;
    }
}
